#include "utils/logger.h"
#include <iomanip>
#include <chrono>

Logger::Logger()
    : m_level(LogLevel::INFO), m_useConsole(true), m_useFile(false) {}

Logger::~Logger() {
    if (m_fileStream.is_open()) {
        m_fileStream.close();
    }
}

Logger& Logger::getInstance() {
    static Logger instance;
    return instance;
}

void Logger::init(LogLevel level, const std::string& logFile) {
    setLevel(level);

    // Set file output.
    if (!logFile.empty()) {
        m_fileStream.open(logFile, std::ofstream::out | std::ofstream::app);
        if (m_fileStream.is_open()) {
            m_useFile = true;
            LOG_INFO("Logging to file: " << logFile);
        } else {
            std::cerr << "Failed to open log file: " << logFile << std::endl;
            m_useFile = false;
        }
    } else {
        m_useFile = false;
    }

    // Default always output to console;
    m_useConsole = true;
}

void Logger::setLevel(LogLevel level) {
    std::lock_guard<std::mutex> lock(m_mutex);
    m_level = level;
}

bool Logger::shouldLog(LogLevel level) const {
    return static_cast<int>(level) >= static_cast<int>(m_level);
}

void Logger::log(LogLevel level, const std::string& message, const char* file, int line) {
    std::lock_guard<std::mutex> lock(m_mutex);

    if (!shouldLog(level)) {
        return;
    }

    std::ostringstream logStream;
    logStream << getCurrentTime() << " "
              << "[" << levelToString(level) << "]"
              <<"[" << file << ":" << line << "]"
              << message << std::endl;
    std::string logEntry = logStream.str();

    // Output to console.
    if (m_useConsole) {
        if (level == LogLevel::ERROR) {
            std::cerr << logEntry;
        } else {
            std::cout << logEntry;
        }
    }

    // Output to file.
    if (m_useFile && m_fileStream.is_open()) {
        m_fileStream << logEntry;
        m_fileStream.flush();
    }
}

std::string Logger::levelToString(LogLevel level) const {
    switch (level) {
        case LogLevel::DEBUG: return "DEBUG";
        case LogLevel::INFO: return "INFO";
        case LogLevel::WARNING: return "WARNING";
        case LogLevel::ERROR: return "ERROR";
        case LogLevel::CRITICAL: return "CRITICAL";
    }
    return "";
}

std::string Logger::getCurrentTime() const {
    auto now = std::chrono::system_clock::now();
    auto in_time_t = std::chrono::system_clock::to_time_t(now);
    auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()) % 1000;

    std::stringstream ss;
    ss << std::put_time(std::localtime(&in_time_t), "%Y-%m-%d %X")
       << '.' << std::setfill('0') << std::setw(3) << ms.count();
    return ss.str();
}